package cn.k7g.alloy.mold.impl;

import cn.k7g.alloy.mold.BaseMold;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.expression.BeanExpressionContextAccessor;
import org.springframework.context.expression.BeanFactoryResolver;
import org.springframework.context.expression.EnvironmentAccessor;
import org.springframework.context.expression.MapAccessor;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.MethodResolver;
import org.springframework.expression.common.TemplateParserContext;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.ReflectivePropertyAccessor;
import org.springframework.expression.spel.support.SimpleEvaluationContext;
import org.springframework.expression.spel.support.StandardEvaluationContext;

import javax.annotation.PostConstruct;
import java.lang.reflect.Method;
import java.util.stream.Collectors;

/**
 * 基于spel实现的模板解析器
 *
 * @date  2021/2/5 下午1:46
 * @author victor-wu
 */
@Slf4j
public class SpelMoldService extends AbstractMoldService {

    final ExpressionParser expressionParser = new SpelExpressionParser();
    SimpleEvaluationContext simpleEvaluationContext;
    StandardEvaluationContext standardEvaluationContext;
    StandardEvaluationContext uncontrolledEvaluationContext;


    final MethodResolver methodResolver = (context, targetObject, name, argumentTypes) -> {
        try {
            DelegateMethod method;
            if (targetObject instanceof GlobalVal) {
                method = globalMethod.get(name).stream().filter(o -> {
                    if (o.originMethod.getParameterCount() != argumentTypes.size()) {
                        return false;
                    }
                    for (int i = 0; i < o.originMethod.getParameterCount(); i++) {
                        if (!o.originMethod.getParameters()[i].getType().isAssignableFrom(argumentTypes.get(i).getType())) {
                            return false;
                        }
                    }
                    return true;
                }).findFirst().orElse(null);
                if (method == null) {
                    throw new NoSuchMethodException("mold 未注册方法: " + name);
                }
            } else {
                Class[] classes = argumentTypes.stream().map(o -> o.getType()).collect(Collectors.toList()).toArray(new Class[0]);
                Method m = targetObject.getClass().getMethod(name, classes);
                method = new DelegateMethod(targetObject, m);
            }
            return new AbstractMoldService.DelegateExecutor(method);
        } catch (NoSuchMethodException e) {
            throw new RuntimeException(e);
        }
    };

    public SpelMoldService() {

    }

    @PostConstruct
    @Override
    public void init() {
        super.init();
        simpleEvaluationContext = new SimpleEvaluationContext.Builder(new ReflectivePropertyAccessor(), new MapAccessor()).build();

        standardEvaluationContext = new StandardEvaluationContext();
        standardEvaluationContext.addPropertyAccessor(new ReflectivePropertyAccessor());
        standardEvaluationContext.addPropertyAccessor(new MapAccessor());
        standardEvaluationContext.addMethodResolver(methodResolver);

        uncontrolledEvaluationContext = new StandardEvaluationContext();
        uncontrolledEvaluationContext.addPropertyAccessor(new ReflectivePropertyAccessor());
        uncontrolledEvaluationContext.addPropertyAccessor(new MapAccessor());
        uncontrolledEvaluationContext.addPropertyAccessor(new EnvironmentAccessor());
        uncontrolledEvaluationContext.addPropertyAccessor(new BeanExpressionContextAccessor());
        uncontrolledEvaluationContext.addMethodResolver(methodResolver);
        uncontrolledEvaluationContext.setBeanResolver(new BeanFactoryResolver(context));
    }


    /**
     * 仅支持数据值的调用，不支持方法调用
     * @param baseMold
     * @return
     */
    @Override
    public Object createContent(BaseMold baseMold) {
        return exec(simpleEvaluationContext, baseMold);
    }

    /**
     * 支持方法的调用, 注意如果有前端提交的内容，不建议使用此方法。可能是造成安全问题。
     * 优先考虑采用 createVal 方法
     * @param baseMold
     * @return
     */
    @Override
    public Object createContentMixUnsafe(BaseMold baseMold) {
        return exec(standardEvaluationContext, baseMold);
    }

    @Override
    public Object createContentUncontrolled(BaseMold baseMold) {
        return exec(uncontrolledEvaluationContext, baseMold);
    }

    public Object exec(EvaluationContext evaluationContext, BaseMold baseMold) {
        GlobalVal params = readyVar(baseMold);
        Object value = expressionParser.parseExpression(baseMold.getContent(), new TemplateParserContext()).getValue(evaluationContext, params);
        if (log.isDebugEnabled()) {
            log.debug("mold执行结果\nmoldContent: {}  \nmoldParams: {}\nmodlResult: {} \n", baseMold.getContent(), params, value);
        }
        return value;
    }
}
